home *** CD-ROM | disk | FTP | other *** search
Wrap
// Name: ComicIt. Converts rough scans of comics into more sharp and colour consistant pictures. // Copyright (C) 1999 S°ren Krarup Olesen // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Please contact me at e-mail sko@kom.auc.dk for questions or comments. #include <math.h> #define STRICT #include <windows.h> #include "ComicIt.h" BOOL bRunningFirstTime=FALSE; struct col { BYTE cRed; BYTE cGreen; BYTE cBlue; }; class SetupGroup { HKEY hKey; DWORD dwType,dwSize,dwWhatHappened; public: Line szInputFile; int nBlackSize; int nEdgeSize; BOOL bStandAlone; int nDepth; int nDarkSize; int nLightSize; Line szOutputFile; SetupGroup(); void init(),save_in_registry(),load_from_registry(); }; SetupGroup::SetupGroup() { RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\ComicIt",0,"SKOClass",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwWhatHappened); lstrcpy(szInputFile,"input.bmp"); nBlackSize=35; nEdgeSize=12; bStandAlone=TRUE; nDepth=3; nDarkSize=12; nLightSize=100; lstrcpy(szOutputFile,"output.bmp"); if (dwWhatHappened==REG_CREATED_NEW_KEY) { bRunningFirstTime=TRUE; save_in_registry(); } else load_from_registry(); RegCloseKey(hKey); hKey=NULL; } void SetupGroup::save_in_registry() { if (!hKey) RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\ComicIt",0,"SKOClass",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwWhatHappened); RegSetValueEx(hKey,"Input file",0,REG_SZ,(CONST LPBYTE)szInputFile,LINE_LEN+1); RegSetValueEx(hKey,"Black size",0,REG_DWORD,(CONST LPBYTE)(&nBlackSize),sizeof(nBlackSize)); RegSetValueEx(hKey,"Edge size",0,REG_DWORD,(CONST LPBYTE)(&nEdgeSize),sizeof(nEdgeSize)); RegSetValueEx(hKey,"Stand alone",0,REG_DWORD,(CONST LPBYTE)(&bStandAlone),sizeof(bStandAlone)); RegSetValueEx(hKey,"Depth",0,REG_DWORD,(CONST LPBYTE)(&nDepth),sizeof(nDepth)); RegSetValueEx(hKey,"Dark size",0,REG_DWORD,(CONST LPBYTE)(&nDarkSize),sizeof(nDarkSize)); RegSetValueEx(hKey,"Light size",0,REG_DWORD,(CONST LPBYTE)(&nLightSize),sizeof(nLightSize)); RegSetValueEx(hKey,"Output file",0,REG_SZ,(CONST LPBYTE)szOutputFile,LINE_LEN+1); if (hKey) RegCloseKey(hKey); } void SetupGroup::load_from_registry() { if (!hKey) RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\ComicIt",0,"SKOClass",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwWhatHappened); while (RegQueryValueEx(hKey,"Input file",0,&dwType,(LPBYTE)szInputFile,&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Black size",0,&dwType,(LPBYTE)(&nBlackSize),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Edge size",0,&dwType,(LPBYTE)(&nEdgeSize),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Stand alone",0,&dwType,(LPBYTE)(&bStandAlone),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Depth",0,&dwType,(LPBYTE)(&nDepth),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Dark size",0,&dwType,(LPBYTE)(&nDarkSize),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Light size",0,&dwType,(LPBYTE)(&nLightSize),&dwSize)!=ERROR_SUCCESS); while (RegQueryValueEx(hKey,"Output file",0,&dwType,(LPBYTE)szOutputFile,&dwSize)!=ERROR_SUCCESS); if (hKey) RegCloseKey(hKey); } BOOL error(HWND hDlg,LPSTR szErrorMessage) { MessageBox(hDlg,szErrorMessage,"Error",MB_ICONERROR); return FALSE; } double len(struct col &p) { double x1,x2,x3,res; x1=(double)p.cRed; x2=(double)p.cGreen; x3=(double)p.cBlue; res=sqrt(x1*x1+x2*x2+x3*x3); return res; } BOOL is_black(struct col &p) { if ((p.cRed==0) && (p.cGreen==0) && (p.cBlue==0)) return TRUE; else return FALSE; } BOOL is_white(struct col &p) { if ((p.cRed==255) && (p.cGreen==255) && (p.cBlue==255)) return TRUE; else return FALSE; } void diff(struct col &in1,struct col &in2,struct col &out) { out.cRed=(BYTE)fabs((double)in1.cRed-in2.cRed); out.cGreen=(BYTE)fabs((double)in1.cGreen-in2.cGreen); out.cBlue=(BYTE)fabs((double)in1.cBlue-in2.cBlue); } BOOL convert(HWND hDlg,SetupGroup &sg) { HANDLE hFile; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; DWORD dwDummy,dwFileSize,dwPictureSize,dwActualPictureSize,dwRed,dwGreen,dwBlue,dwNumberOfAvgPixels; LONG i,j,k,lExtraBytesPerLine,lBegin,lEnd; BYTE cDummy; struct col **p,**q,**r; SetDlgItemText(hDlg,IDC_STATUS,"Importing BMP file..."); hFile=CreateFile(sg.szInputFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile==INVALID_HANDLE_VALUE) return error(hDlg,"Cannot open input file"); dwFileSize=GetFileSize(hFile,NULL); ReadFile(hFile,&bmfh,sizeof(bmfh),&dwDummy,NULL); if (bmfh.bfType!=19778) { CloseHandle(hFile); return error(hDlg,"Input is not a BMP file"); } if (bmfh.bfSize!=dwFileSize) { // DWORD CloseHandle(hFile); return error(hDlg,"Wrong file size field in BMP file"); } if (bmfh.bfReserved1||bmfh.bfReserved2) { if (MessageBox(hDlg,"Unexpected content in reserved\nfields in BMP file.\n\nDo you want to continue?","Warning",MB_ICONWARNING|MB_YESNO)==IDNO) { CloseHandle(hFile); return FALSE; } } ReadFile(hFile,&bmih,sizeof(bmih),&dwDummy,NULL); if ((bmih.biWidth<32)||(bmih.biHeight)<32) { // LONG - LONG CloseHandle(hFile); return error(hDlg,"The BMP file is too small for conversion"); } if (bmih.biPlanes!=1) { CloseHandle(hFile); return error(hDlg,"Unsupported number of planes in BMP file"); } if (bmih.biBitCount!=24) { CloseHandle(hFile); return error(hDlg,"The input BMP file must be 24 bits"); } if (bmih.biCompression!=BI_RGB) { CloseHandle(hFile); return error(hDlg,"The input BMP file must not be compressed"); } dwPictureSize=dwFileSize-sizeof(bmfh)-sizeof(bmih); if (dwPictureSize%bmih.biHeight) { CloseHandle(hFile); return error(hDlg,"Picture size is not dividable\nby the number of scan lines"); } dwActualPictureSize=bmih.biWidth*bmih.biHeight*3; lExtraBytesPerLine=(dwPictureSize-dwActualPictureSize)/bmih.biHeight; p=new struct col *[bmih.biWidth]; if (!p) FatalAppExit(0,"Not enough memory"); for (i=0; i<bmih.biWidth; i++) { p[i]=new struct col[bmih.biHeight]; if (!p[i]) FatalAppExit(0,"Not enough memory"); } for (j=0; j<bmih.biHeight; j++) { for (i=0; i<bmih.biWidth; i++) ReadFile(hFile,&p[i][j],3,&dwDummy,NULL); for (i=0; i<lExtraBytesPerLine; i++) ReadFile(hFile,&cDummy,1,&dwDummy,NULL); } CloseHandle(hFile); SetDlgItemText(hDlg,IDC_STATUS,"Mark-up regions..."); q=new struct col *[bmih.biWidth]; if (!q) FatalAppExit(0,"Not enough memory"); for (i=0; i<bmih.biWidth; i++) { q[i]=new struct col[bmih.biHeight]; if (!q[i]) FatalAppExit(0,"Not enough memory"); } for (j=0; j<bmih.biHeight; j++) for (i=0; i<bmih.biWidth; i++) q[i][j].cRed=q[i][j].cGreen=q[i][j].cBlue=255; for (j=0; j<bmih.biHeight; j++) for (i=0; i<bmih.biWidth; i++) if (len(p[i][j])<(441.68*(sg.nBlackSize/100.0))) q[i][j].cRed=q[i][j].cGreen=q[i][j].cBlue=0; struct col c1,c2,c3; for (j=1; j<bmih.biHeight-1; j++) { for (i=1; i<bmih.biWidth-1; i++) { diff(p[i-1][j],p[i][j],c1); diff(p[i][j-1],p[i][j],c2); if (len(c1)>len(c2)) c3=c1; // Finds the largest value--either H or V. Could also use the divergence?! else c3=c2; if (len(c3)>(441.68*(sg.nEdgeSize/100.0))) q[i][j].cRed=q[i][j].cGreen=q[i][j].cBlue=0; } } SetDlgItemText(hDlg,IDC_STATUS,"Noise reduction..."); if (sg.bStandAlone) { for (j=1; j<bmih.biHeight-1; j++) for (i=1; i<bmih.biWidth-1; i++) if (is_black(q[i][j])) if (is_white(q[i-1][j]) && is_white(q[i+1][j]) && is_white(q[i][j-1]) && is_white(q[i][j+1])) q[i][j].cRed=q[i][j].cGreen=q[i][j].cBlue=255; } SetDlgItemText(hDlg,IDC_STATUS,"Save b/w picture..."); hFile=CreateFile("bw.bmp",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile==INVALID_HANDLE_VALUE) return error(hDlg,"Cannot open b/w file"); WriteFile(hFile,&bmfh,sizeof(bmfh),&dwDummy,NULL); WriteFile(hFile,&bmih,sizeof(bmih),&dwDummy,NULL); cDummy='\0'; for (j=0; j<bmih.biHeight; j++) { for (i=0; i<bmih.biWidth; i++) WriteFile(hFile,&q[i][j],3,&dwDummy,NULL); for (i=0; i<lExtraBytesPerLine; i++) WriteFile(hFile,&cDummy,1,&dwDummy,NULL); } CloseHandle(hFile); SetDlgItemText(hDlg,IDC_STATUS,"Colour averaging..."); r=new struct col *[bmih.biWidth]; if (!r) FatalAppExit(0,"Not enough memory"); for (i=0; i<bmih.biWidth; i++) { r[i]=new struct col[bmih.biHeight]; if (!r[i]) FatalAppExit(0,"Not enough memory"); } for (j=0; j<bmih.biHeight; j++) { for (i=1; i<bmih.biWidth; i++) { r[i][j].cRed=p[i][j].cRed; r[i][j].cGreen=p[i][j].cGreen; r[i][j].cBlue=p[i][j].cBlue; } } for (k=0; k<sg.nDepth; k++) { // Vertical: bmih.biHeight--; for (j=0; j<bmih.biWidth; j++) { lBegin=lEnd=0; while (1) { for (lEnd=lBegin; (is_white(q[j][lEnd]) && lEnd<bmih.biHeight); lEnd++); dwRed=dwGreen=dwBlue=0; for (i=lBegin; i<lEnd; i++) { dwRed+=r[j][i].cRed; dwGreen+=r[j][i].cGreen; dwBlue+=r[j][i].cBlue; } dwNumberOfAvgPixels=lEnd-lBegin; if (!dwNumberOfAvgPixels) goto next_try2; dwRed/=dwNumberOfAvgPixels; dwGreen/=dwNumberOfAvgPixels; dwBlue/=dwNumberOfAvgPixels; for (i=lBegin; i<lEnd; i++) { r[j][i].cRed=(BYTE)dwRed; r[j][i].cGreen=(BYTE)dwGreen; r[j][i].cBlue=(BYTE)dwBlue; } next_try2: lBegin=lEnd+1; // perhaps +1 if (lBegin>=bmih.biHeight) break; // EOL reached } } bmih.biHeight++; // Horizontal: bmih.biWidth--; for (j=0; j<bmih.biHeight; j++) { lBegin=lEnd=0; while (1) { for (lEnd=lBegin; (is_white(q[lEnd][j]) && lEnd<bmih.biWidth); lEnd++); dwRed=dwGreen=dwBlue=0; for (i=lBegin; i<lEnd; i++) { dwRed+=r[i][j].cRed; dwGreen+=r[i][j].cGreen; dwBlue+=r[i][j].cBlue; } dwNumberOfAvgPixels=lEnd-lBegin; if (!dwNumberOfAvgPixels) goto next_try1; dwRed/=dwNumberOfAvgPixels; dwGreen/=dwNumberOfAvgPixels; dwBlue/=dwNumberOfAvgPixels; for (i=lBegin; i<lEnd; i++) { r[i][j].cRed=(BYTE)dwRed; r[i][j].cGreen=(BYTE)dwGreen; r[i][j].cBlue=(BYTE)dwBlue; } next_try1: lBegin=lEnd+1; if (lBegin>=bmih.biWidth) break; } } bmih.biWidth++; } SetDlgItemText(hDlg,IDC_STATUS,"Final b/w tuning..."); for (j=0; j<bmih.biHeight; j++) for (i=0; i<bmih.biWidth; i++) if (len(p[i][j])<(441.68*(sg.nDarkSize/100.0))) r[i][j].cRed=r[i][j].cGreen=r[i][j].cBlue=0; for (j=0; j<bmih.biHeight; j++) for (i=0; i<bmih.biWidth; i++) if (len(p[i][j])>(441.68*(sg.nLightSize/100.0))) r[i][j].cRed=r[i][j].cGreen=r[i][j].cBlue=255; SetDlgItemText(hDlg,IDC_STATUS,"Save output picture..."); hFile=CreateFile(sg.szOutputFile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile==INVALID_HANDLE_VALUE) return error(hDlg,"Cannot open output file"); WriteFile(hFile,&bmfh,sizeof(bmfh),&dwDummy,NULL); WriteFile(hFile,&bmih,sizeof(bmih),&dwDummy,NULL); cDummy='\0'; for (j=0; j<bmih.biHeight; j++) { for (i=0; i<bmih.biWidth; i++) WriteFile(hFile,&r[i][j],3,&dwDummy,NULL); for (i=0; i<lExtraBytesPerLine; i++) WriteFile(hFile,&cDummy,1,&dwDummy,NULL); } CloseHandle(hFile); for (i=0; i<bmih.biWidth; i++) delete r[i]; delete r; for (i=0; i<bmih.biWidth; i++) delete q[i]; delete q; for (i=0; i<bmih.biWidth; i++) delete p[i]; delete p; return TRUE; } BOOL invalid_vector_size(HWND hDlg,int nSize) { if ((nSize>=0)&&(nSize<=100)) return FALSE; MessageBox(hDlg,"The length of an RGB vector should be kept\nbetween 0 and 100%, where 0 corresponds\nto RGB=0,0,0 (length=0) and 100% corresponds\nto RGB=255,255,255 (length=442)","Range error",MB_ICONERROR); return TRUE; } BOOL CALLBACK MainProc(HWND hDlg,UINT wMsg,WPARAM wParam,LPARAM) { static SetupGroup sg; static SetupGroup _sg; switch (wMsg) { case WM_INITDIALOG: if (bRunningFirstTime) MessageBox(hDlg,"ComicIt version 1.0, Copyright (C) 1999 S°ren Krarup Olesen\nComicIt comes with ABSOLUTELY NO WARRANTY; for details\nsee the file gpl.txt. This is free software, and you are welcome\nto redistribute it under certain conditions; see the file gpl.txt for details.","License",MB_ICONINFORMATION); SetDlgItemText(hDlg,IDC_INPUT_FILE,sg.szInputFile); SetDlgItemInt(hDlg,IDC_BLACK_SIZE,sg.nBlackSize,TRUE); SetDlgItemInt(hDlg,IDC_EDGE_SIZE,sg.nEdgeSize,TRUE); if (sg.bStandAlone) SendDlgItemMessage(hDlg,IDC_STAND_ALONE,BM_SETCHECK,BST_CHECKED,0); else SendDlgItemMessage(hDlg,IDC_STAND_ALONE,BM_SETCHECK,BST_UNCHECKED,0); if (sg.nDepth==1) SendDlgItemMessage(hDlg,IDC_DEPTH_1,BM_SETCHECK,BST_CHECKED,0); else if (sg.nDepth==2) SendDlgItemMessage(hDlg,IDC_DEPTH_2,BM_SETCHECK,BST_CHECKED,0); else SendDlgItemMessage(hDlg,IDC_DEPTH_3,BM_SETCHECK,BST_CHECKED,0); SetDlgItemInt(hDlg,IDC_DARK_SIZE,sg.nDarkSize,TRUE); SetDlgItemInt(hDlg,IDC_LIGHT_SIZE,sg.nLightSize,TRUE); SetDlgItemText(hDlg,IDC_OUTPUT_FILE,sg.szOutputFile); _sg=sg; break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_CONVERT: GetDlgItemText(hDlg,IDC_INPUT_FILE,_sg.szInputFile,LINE_LEN); _sg.nBlackSize=GetDlgItemInt(hDlg,IDC_BLACK_SIZE,NULL,TRUE); if (invalid_vector_size(hDlg,_sg.nBlackSize)) break; _sg.nEdgeSize=GetDlgItemInt(hDlg,IDC_EDGE_SIZE,NULL,TRUE); if (invalid_vector_size(hDlg,_sg.nEdgeSize)) break; if (SendDlgItemMessage(hDlg,IDC_STAND_ALONE,BM_GETCHECK,0,0)==BST_CHECKED) _sg.bStandAlone=TRUE; else _sg.bStandAlone=FALSE; if (SendDlgItemMessage(hDlg,IDC_DEPTH_1,BM_GETCHECK,0,0)==BST_CHECKED) _sg.nDepth=1; else if (SendDlgItemMessage(hDlg,IDC_DEPTH_2,BM_GETCHECK,0,0)==BST_CHECKED) _sg.nDepth=2; else _sg.nDepth=3; _sg.nDarkSize=GetDlgItemInt(hDlg,IDC_DARK_SIZE,NULL,TRUE); if (invalid_vector_size(hDlg,_sg.nDarkSize)) break; _sg.nLightSize=GetDlgItemInt(hDlg,IDC_LIGHT_SIZE,NULL,TRUE); if (invalid_vector_size(hDlg,_sg.nLightSize)) break; GetDlgItemText(hDlg,IDC_OUTPUT_FILE,_sg.szOutputFile,LINE_LEN); if (convert(hDlg,_sg)) sg=_sg; SetDlgItemText(hDlg,IDC_STATUS,"OK"); break; case IDC_EXIT: sg.save_in_registry(); EndDialog(hDlg,FALSE); break; default: return FALSE; } break; case WM_CLOSE: sg.save_in_registry(); EndDialog(hDlg,FALSE); break; default: return FALSE; } return TRUE; } WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow) { DialogBox(hInst,"MAIN",HWND_DESKTOP,MainProc); return FALSE; }